package com.intertid.mongodb
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import org.apache.commons.beanutils.PropertyUtils
import com.intertid.mongodb.annotations.Transient
import com.intertid.mongodb.factory.MongoDBTemplateFactory
import org.slf4j.LoggerFactory
import com.intertid.mongodb.commons.MongoDBUtils
import java.beans.PropertyDescriptor
import net.sf.cglib.proxy.MethodInterceptor
import net.sf.cglib.proxy.MethodProxy
import net.sf.cglib.proxy.Enhancer
import com.intertid.scala.commons._
import org.bson.types.ObjectId
import net.sf.cglib.core.ClassEmitter

class LazyLoadDynamicProxy(ref: RefBean) extends InvocationHandler with MethodInterceptor {

  val log = LoggerFactory.getLogger(classOf[LazyLoadDynamicProxy]);

  val sourceClass = Class.forName(ref.getClassName());

  val properties = PropertyUtils.getPropertyDescriptors(sourceClass).filter(p => {
    try {
      !sourceClass.getDeclaredField(p.getName()).isAnnotationPresent(classOf[Transient]);
    } catch {
      case e: java.lang.NoSuchFieldException => {
        false;
      }
    }

  });

  val template = MongoDBTemplateFactory.getCurrentTemplate();

  def createProxy(): Object = {
    val enhancer = new Enhancer;
    enhancer.setSuperclass(sourceClass);
    enhancer.setInterfaces(Array(classOf[ProxyTag]));
    enhancer.setCallback(this);

    enhancer.create();
  }

  @throws(classOf[Throwable])
  def intercept(proxy: Object, method: Method, params: Array[Object], methodProxy: MethodProxy): Object = {
    invoke(proxy, method, params);
  }

  override def invoke(proxy: Object, method: Method, params: Array[Object]): Object = {

    val found = properties.find(pd => { pd.getWriteMethod().equals(method) || pd.getReadMethod().equals(method) });
    if (!found.isEmpty) {

      val p = found.get
      val isSet = p.getWriteMethod().equals(method);
      val isGet = p.getReadMethod().equals(method);
      if (isGet) { //

        val value =
          if (ref.souce == null) {
            load(p);
          } else {
            ref.souce;
          }

        return method.invoke(value);
      }

      if (isSet) {
        val value =
          if (ref.souce == null) {
            load(p);
          } else {
            ref.souce;
          }

        //        val targetProp = PropertyUtils.getPropertyDescriptor(ref.souce, p.getName());
        //        targetProp.getWriteMethod().invoke(ref.souce, params);

        method.invoke(value, params: _*);
      }
    }

    null;
  }

  def load(p: PropertyDescriptor): Object = {
    if (ref.dbref != null) {

      val dbobj = ref.dbref.fetch();
      if (dbobj != null) {

        val bean = MongoDBUtils.dbObject2Bean(dbobj, sourceClass);

        ref.souce = bean;

        return bean;
      }
    }
    val collectionName = Mapper.getCollectionName(sourceClass);
    return if (collectionName == null) {
      log.warn(sourceClass.getName() + "不存在的Collection");
      null;
    } else {
      val id = ref.`get$id`();

      val foundVal = template.getSession()(collectionName).findOneByID((if (id.isInstanceOf[String]) new ObjectId(id.toString()) else id));
      if (!foundVal.isEmpty) {
        val dbobj = foundVal.get;
        val bean = MongoDBUtils.dbObject2Bean(dbobj, sourceClass);
        ref.souce = bean;
        bean;
      } else {
        null;
      }
    }
  }
}